技巧72 通过受限连接交付镜像

即使使用了分层,推送和拉取Docker镜像仍然是一个耗费带宽的过程。在具有免费大带宽连接的世界,这不成问题,但现实有时会迫使我们去处理两个数据中心之间低宽带连接或昂贵的宽带计费的问题。在这种情况下,需要找到一种更加高效的传输差异部分的方法,否则一天运行多次流水线的CD憧憬将遥不可及。

理想的解决方案是使用一个可以降低镜像的平均尺寸的工具,甚至可以比典型的压缩方法做得更小。

问题

想要在两台使用低带宽连接的机器之间复制镜像。

解决方案

导出镜像,进行拆分,传输块,并在另一端导入重新组合的镜像。

为此,首先需要介绍一个新的工具bup。它是作为一个备份工具创造出来的,具有极其高效的去重能力—— 去重能力 是指识别重复使用的数据,只存储一份副本的能力。它对于包括大量相似文件的存档文件尤其有效,而这正是Docker导出镜像所允许的一种格式。

我们为本技巧创建了一个名为dbup(“Docker bup”的简称)的镜像,可更容易地使用 bup 对镜像进行去重。可在https://github.com/docker-in-practice/dbup找到其背后的代码。

作为一个示范,来看看从ubuntu:14.04.1镜像升级到ubuntu:14.04.2时可以节省多少宽带。需要牢记的一点是,在现实中这些镜像上面都有多个层,Docker会在下面的层变更时进行完全的重新传输。相比之下,本技巧将识别出高度的相似性,节省的带宽比下面示例中看到的要大。

第一步是把两个镜像都拉取下来,以便查看通过网络传输了多少流量,如代码清单9-2所示。

代码清单9-2 检查或保存两个Ubuntu镜像

$ docker pull ubuntu:14.04.1 && docker pull ubuntu:14.04.2
[...]
$ docker history ubuntu:14.04.1
IMAGE        CREATED     CREATED BY                                    SIZE
ab1bd63e0321 2 years ago /bin/sh -c #(nop) CMD [/bin/bash]             0B
<missing>    2 years ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\... 1.9kB
<missing>    2 years ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/po... 195kB
<missing>    2 years ago /bin/sh -c #(nop) ADD file:62400a49cced0d7... 188MB
<missing>    4 years ago                                               0B
$ docker history ubuntu:14.04.2
IMAGE        CREATED     CREATED BY                                    SIZE
44ae5d2a191e 2 years ago /bin/sh -c #(nop) CMD ["/bin/bash"]           0B
<missing>    2 years ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\... 1.9kB
<missing>    2 years ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/po... 195kB
<missing>    2 years ago /bin/sh -c #(nop) ADD file:0a5fd3a659be172... 188MB
$ docker save ubuntu:14.04.1 | gzip | wc -c
65973497
$ docker save ubuntu:14.04.2 | gzip | wc -c
65994838

每个镜像的底层(ADD这一层)占据了大部分尺寸,可以看到添加的文件有所不同,因此可以把整个镜像尺寸作为推送新镜像时需要传输的大小。同样需要注意的是,Docker注册中心使用gzip压缩来传输层,因此在测量时也将其加入(而不是从 docker history 获得尺寸)。在初始部署及后续部署中都大概传输了65 MB。

在开始前,需要准备两样东西—— 一个bup作为存储器用于存储数据“池”的目录,以及dockerinpractice/dbup镜像。然后就可以将镜像添加到bup数据池中了,如代码清单9-3所示。

代码清单9-3 将两个Ubuntu镜像保存到bup数据池中

$ mkdir bup_pool
$ alias dbup="docker run --rm \
    -v $(pwd)/bup_pool:/pool -v /var/run/docker.sock:/var/run/docker.sock \
    dockerinpractice/dbup"
$ dbup save ubuntu:14.04.1
Saving image!
Done!
$ du -sh bup_pool
74M     bup_pool
$ dbup save ubuntu:14.04.2
Saving image!
Done!
$ du -sh bup_pool
96M     bup_pool

将第二个镜像添加到bup数据池中只增加了大概20 MB的大小。假设在添加ubuntu:14.04.1后已经将这个目录同步到另一台机器上(可能使用的是rsync),再次同步这个目录将只传输20 MB(而不是之前的65 MB)。

在另一端需要如代码清单9-4这样加载镜像。

代码清单9-4 从bup数据池加载镜像

$ dbup load ubuntu:14.04.1
Loading image!
Done!

在两个注册中心之间传输的过程将类似下面这样:

(1)在主机1上 docker pull

(2)在主机1上 dbup save

(3)从主机1 rsync 到主机2;

(4)在主机2上 dbup load

(5)在主机2上 docker push

本技巧将之前的很多不可能变成了可能。例如,现在可以重新安排和合并层,而无须考虑通过低带宽连接传输所有的新层需要花费多长时间。

讨论

即使是遵从最佳实践将应用程序代码作为最后一个环节进行添加,bup也能帮上忙——它将识别出大部分未修改的代码,而只将差异部分添加到数据池中。

数据池可以非常大,比如数据库文件等,而bup的执行效果会很好(如果你决定采用技巧77在容器里使用数据库,这一点尤其有用,意味着不需要用到数据卷)。事实上,这有点超乎寻常——数据库导出和备份对增量传输而言是非常高效的,但是数据库的实际磁盘存储可能会有很大不同,有时甚至会让rsync这类工具受挫。除此之外,dbup会让镜像完整历史唾手可得——无须为了回滚而存储3个完整的镜像副本。你可以从容地将它们从数据池里拉取出来。遗憾的是,目前没有办法可以把你不再需要的镜像从数据池中清除,因此你可能需要时不时地清理一下数据池。

虽然读者对dbup可能没有迫切的需要,但请在宽带账单开始上升时记起这一点!

results matching ""

    No results matching ""